#![allow(warnings)]
use std::fmt;

use super::Object;

#[derive(Clone, Debug, PartialEq)]
enum SlotType {
    Num,  // i32
    Ref,  // TODO *mut Object 指针现未进行回收处理
    Null, // null
}

impl Default for SlotType {
    fn default() -> Self {
        Self::Null
    }
}

// 变量槽，虚拟机为局部变量分配内存所使用的最小单位
// 于byte、char、float、int、short、boolean和returnAddress等长度不超过32位的数据类型，每个局部变量占用一个变量槽
// double和long这两种64位的数据类型则需要两个变量槽来存放。
// 方法参数（包括实例方法中的隐藏参数“this”）、
// 显式异常处理程序的参数（Exception Handler Parameter，就是try-catch语句中catch块中所定义的异常）、
// 方法体中定义的局部变量都需要依赖局部变量表来存放。
// 用usize存储数值和引用
#[derive(Clone, Debug, Default)]
pub struct Slot {
    slot_type: SlotType, 
    val: usize, // 使用usize存储int与object指针，根据slot_type进行转换
}

impl Slot {
    pub fn get_num(&self) -> i32 {
        match self.slot_type {
            SlotType::Num => self.val as i32,
            SlotType::Ref => panic!("slot type is not Num"),
            SlotType::Null => 0, // 默认值暂设为0
        }
    }

    pub fn set_num(&mut self, val: i32) {
        self.val = val as usize;
        self.slot_type = SlotType::Num;
    }

    pub fn take_num(&mut self) -> i32 {
        let val = self.get_num();
        self.slot_type = SlotType::Null;
        val
    }

    pub fn get_ref(&self) -> Option<*mut Object> {
        match self.slot_type {
            SlotType::Num => panic!("slot type is not Ref"),
            SlotType::Ref => Some(unsafe { std::mem::transmute(self.val) }),
            SlotType::Null => None,
        }
    }

    pub fn set_ref(&mut self, val: Option<*mut Object>) {
        if let Some(obj) = val {
            self.val = obj as usize;
            self.slot_type = SlotType::Ref;
        } else {
            self.slot_type = SlotType::Null;
        }
    }

    pub fn take_ref(&mut self) -> Option<*mut Object> {
        let val = self.get_ref();
        self.slot_type = SlotType::Null;
        val
    }

    pub fn take(&mut self) -> Slot {
        let this = self.clone();
        self.slot_type = SlotType::Null;
        this
    }

    pub fn set_null(&mut self) {
        self.slot_type = SlotType::Null;
    }

    pub fn is_null(&self) -> bool {
        self.slot_type == SlotType::Null
    }
}

impl fmt::Display for Slot {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let s = match self.slot_type {
            SlotType::Num => format!("Num: {}", self.val),
            SlotType::Ref => {
                let obj = self.get_ref().unwrap();
                let obj_name = unsafe { &*{ &*obj }.get_class() }.get_name();
                format!("Ref: {}", obj_name)
            },
            SlotType::Null => format!("Null"),
        };
        write!(f, "{}", s)
    }
}

#[derive(Default, Debug, Clone)]
pub struct Slots(Vec<Slot>);

impl Slots {
    pub fn new_slots(size: usize) -> Slots {
        Slots((0..size).map(|_| Slot::default()).collect())
    }
    
    pub fn set_int(&mut self, index:usize, val: i32) {
        self.0[index].set_num(val);
    }

    pub fn get_int(&self, index: usize) -> i32 {
        self.0[index].get_num()
    }

    pub fn set_float(&mut self, index: usize, val: f32) {
        let bytes = val.to_be_bytes();
        self.0[index].set_num(i32::from_be_bytes(bytes));
    }

    pub fn get_float(&self, index: usize) -> f32 {
        let bytes = self.0[index].get_num().to_be_bytes();
        f32::from_be_bytes(bytes)
    }

    pub fn set_long(&mut self, index: usize, val: i64) {
        self.0[index].set_num((val >> 32) as i32);
        self.0[index+1].set_num(val as i32);
    }

    pub fn get_long(&self, index: usize) -> i64 {
        // 小转大，i32 -> i64
        // 如果源数据是无符号的，则进行零扩展(zero-extend)
        // 如果源数据是有符号的，则进行符号扩展(sign-extend)
        let high = self.get_int(index) as i64;
        let low = self.get_int(index + 1) as i64;
        (high << 32) | (low & 0xffffffff)
    }

    pub fn set_double(&mut self, index: usize, val: f64) {
        let bytes = val.to_be_bytes();
        self.set_long(index, i64::from_be_bytes(bytes));
    }

    pub fn get_double(&self, index: usize) -> f64 {
        let bytes = self.get_long(index).to_be_bytes();
        f64::from_be_bytes(bytes)
    }

    pub fn set_ref(&mut self, index: usize, _ref: Option<*mut Object>) {
        self.0[index].set_ref(_ref);
    }

    pub fn get_ref(&self, index: usize) -> Option<*mut Object> {
        self.0[index].get_ref()
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }
}
